function unimplemented(message = "not implemented") {
  throw new Error(message);
}

function unreachable() {
  throw new Error(unreachable);
} 

class TypeBase {
  constructor(loc) {
    this.loc = loc;
  }
  name() {
    unreachable();
  }
  opeartorPlus() {
    return false;
  }
}

class TypeNumber extends TypeBase {
  name() {
    return 'number';
  }
  opeartorPlus(right) {
    return right instanceof TypeNumber;
  }
}

class TypeRegExp extends TypeBase {
  name() {
    return 'RegExp';
  }
}

// 原始类型检测
export function typecheck(root) {
  const visitor = {
    NumbericLiteral({ loc }) {
      return new TypeNumber(loc);
    },
    RegExpToken({ loc }) {
      return new TypeRegExp(loc);
    },
    BinaryExpression({ left, operatorToken: op, right, loc }) {
      const leftType = visit(left);
      const rightType = visit(right);

      const spanLoc = {
        file: left.loc.file,
        start: left.loc.start,
        end: right.loc.end
      };

      switch (op.type) {
        case "PlusToken":
          // if (!(leftType instanceof TypeNumber)) {
          //   throw new Error(`lefthandside argument to binary operator "${op.type}" must be of type "${TypeNumber.name}" at ${leftType.loc.file}:${leftType.loc.start.line}:${leftType.loc.start.column}`);
          // }
          // if (!(rightType instanceof TypeNumber)) {
          //   throw new Error(`righthandside argument to binary operator "${op.type}" must be of type "${TypeNumber.name}" at ${rightType.loc.file}:${rightType.loc.start.line}:${rightType.loc.start.column}`);
          // }

          if (!leftType.opeartorPlus(rightType)) {
            throw new Error(`Operator "+" cannot be applied to types "${leftType.name()}" and "${rightType.name()}".`)
          }
          return new TypeNumber(spanLoc);
        case "MulToken":
        case "DivToken":
          unimplemented();
        default:
          throw new Error(`unknow operator "${op.type}" at ${op.loc.file}:${op.loc.start.line}:${op.loc.start.column}`);
      }
    }
  };

  const visit = (node) => {
    if (node == null) throw "something";
    const handler = visitor[node.type];
    if (!handler) {
      throw new Error(`unknow node type "${node.type}" at ${node.loc.file}:${node.loc.start.line}:${node.loc.start.column}`);
    }

    return handler(node);
  }

  return visit(root);
}